package com.hwd.gmall.common.aspect;

import com.rabbitmq.client.Channel;
import lombok.extern.log4j.Log4j2;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.amqp.core.Message;
import org.springframework.stereotype.Component;

/**
 * 消息队列监听切面类
 *
 * @author 黄伟东/Victor
 * @date 2022/05/14 13:55
 */
@Component
@Aspect
@Log4j2
public class MqListenerAspect {

    @Around("@annotation(com.hwd.gmall.common.aspect.ListenMessage)")
    public Object cacheAroundAdvice(ProceedingJoinPoint point) {

        // 返回结果初始化
        Object result = null;

        // 获取方法的签名
        MethodSignature signature = (MethodSignature) point.getSignature();
        // 通过方法签名获取方法上的注解
        ListenMessage listenMessage = signature.getMethod().getAnnotation(ListenMessage.class);
        // 要监听的消息的名字
        String messageDescription = listenMessage.messageDescription();

        // 获取的参数值数组
        Object[] args = point.getArgs();
        Message message = null;
        Channel channel = null;

        for (Object arg : args) {
            if (arg instanceof Message) {
                message = (Message) arg;
            } else if (arg instanceof Channel) {
                channel = (Channel) arg;
            }
        }

        if (message == null || channel == null) {
            return null;
        }

        // 获取消息的编号
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        // 获取消息内容
        String messageString = new String(message.getBody());

        try {
            // 执行方法
            result = point.proceed(point.getArgs());
            // 确认消息
            channel.basicAck(deliveryTag, false);
        } catch (Throwable throwable) {
            try {
                // 判断是否第一次消费
                if (message.getMessageProperties().getRedelivered()) {
                    // 第二次消费失败，丢弃消息，不放回队列
                    log.error("连续两次消费" + messageDescription + "失败，失败的内容是: " + messageString);
                    log.error("连续两次消费" + messageDescription + "失败，失败的原因是: " + throwable.getMessage());
                    channel.basicReject(deliveryTag, false);
                }
                // 第一次消费，重试一次
                channel.basicReject(deliveryTag, true);
            } catch (Exception ex) {
                log.error("拒绝消息失败，失败的原因是: " + ex.getMessage());
                log.error("拒绝消息失败，失败的内容是: " + messageString);
            }
        }

        return result;
    }


}
